Skip to content

Ctrl+Space 被三个模块同时注册,静默覆盖

状态: 🟢 已修复

日期: 2026-07-10 严重程度: 🔴 P1(功能静默失效,用户无法通过快捷键访问功能) 影响范围: frontend/src/core/main.ts + scene.ts + outfit.ts发现方式: 代码搜索(手动审计) 修复方案: shortcut-registry.tsShortcutRegistry 类,注册时检查冲突,先注册的生效


问题描述

Ctrl+Space 在三个模块里被同时注册:

  1. main.ts — 切换动作模式
  2. scene.ts — 切换相机模式
  3. outfit.ts — 切换服装预设

浏览器的事件系统:后注册的监听覆盖先注册的。所以最后一个注册的 Ctrl+Space 生效,前两个被静默覆盖。

用户按 Ctrl+Space,想切动作模式——切的是服装预设。用户按 Ctrl+Space,想切相机模式——切的是服装预设。

没有任何报错。没有任何提示。快捷键只是"不工作了"。

根因分析

快捷键注册是分散式的。每个模块自己 document.addEventListener('keydown', ...),没有人管别的模块用什么键。

冲突检测不存在。注册冲突从运行时提前到了设计时——但没人检查设计时。

为什么没有暴露

注册快捷键时,开发者只关心"我的快捷键能工作",不会去看别的模块有没有用同一个键。

这是协作型 bug——单独看每个模块的代码,都是正确的。只有把它们放在一起,才会发现冲突。

修复方案

src/core/shortcut-registry.tsShortcutRegistry 类。

注册时检查冲突:如果同一个 key 已经被注册,记录冲突来源并打印 console.warn。先注册的生效,后注册的被忽略。

注销需要同时匹配 key 和 source——防止误删其他模块注册的同一个键。

Ctrl+Space 的冲突被发现后,scene.tsoutfit.ts 的快捷键改为其他 key。

教训

  1. 协作型 bug 是最难发现的 — 单独看每个模块,都是正确的。冲突只存在于"放在一起"的时候
  2. 静默覆盖比报错更危险 — 报错至少告诉你哪里有问题。静默覆盖让你以为功能还在